<template>
  <div style="width: 500px">
    <div style="display: flex;justify-content: space-between">
      <div>

        <el-input v-model="filterText" placeholder="请输入内容"></el-input>
      </div>


    </div>

    <div>
      <!-- defaultProps 相当于映射关系,label 对应 name, children对应json的children     -->
      <el-tree ref="tree" :data="deps" :props="defaultProps" :filter-node-method="filterNode"
               :expand-on-click-node="false">
        <!--        使用 scoped slot 会传入两个参数node和data，分别表示当前节点的 Node 对象和当前节点的数据-->
        <!--     node 就是 el-tree 中的一个树枝     data 是我们返回的 json 中的一个部门     -->
        <!--        即 node里有  lable 和 children   data里有 id name parentid dapepath enabled ....-->
        <span class="custom-tree-node" slot-scope="{ node, data }"
              style="display: flex ; justify-content: space-between ;width: 100%">
<!--        这里span作用已经作为展示树节点和和其他元素的作用-->
          <!--            <span>{{ node.label }}</span>-->
          <!--            用  data.name 和 node.label 一样效果-->
            <span>{{ data.name }}</span>
        <span>
          <el-button
              type="text"
              size="mini"
              @click="showAddDepView(data)">
<!--            这个data 就是slot-scope="{ node, data } 的这个data ，一个部门的json。包含children-->
            添加
          </el-button>
          <el-button
              type="text"
              size="mini"
              @click="deleteDep(data)">
<!--        ui示例反而比较清楚看到    @click="() => remove(node, data)">  data 就是slot-scope="{ node, data }的data-->
            删除
          </el-button>
        </span>
      </span>
      </el-tree>
    </div>
    <div>
      <el-dialog
          title="提示"
          :visible.sync="dialogVisible"
          width="30%">

        <table>
          <tr>
            <td>父部门：</td>
            <td>
              <el-select v-model="dep.parentId" placeholder="请选择">
                <!--      v-model 对应的是某个el-option 的  value   一开始打开对话时先显示 dep.parentId所的意义的options如果选择了其他像，则     v-model的值就变为 所选择项的value值，这里即 item.id -->
                <!--            v-for="item in allDeps" data（）{}里定义的变量 通常是数组 这里从数据库查所有部门。不分父子。
                                    :key="item.id"  用于响应事件区分
                                    :label="item.name" 用展示的值
                                    :value="item.id"    实际获取的值。    -->
                <el-option
                    v-for="item in allDeps"
                    :key="item.id"
                    :label="item.name"
                    :value="item.id">
                </el-option>
              </el-select>
            </td>
          </tr>
          <tr>
            <td>部门名称</td>
            <td>
              <el-input v-model="dep.name" placeholder="请输入内容"></el-input>
              <!--   v-model="dep.name 双向绑定，用户填的数据封装到dep中。           -->
            </td>
          </tr>
        </table>

        <span slot="footer" class="dialog-footer">
    <el-button @click="dialogVisible = false">取 消</el-button>
    <el-button type="primary" @click="doAddDepartment">确 定</el-button>
  </span>
      </el-dialog>
    </div>
  </div>
</template>

<script>
import {getRequest} from "@/util/api";

export default {
  name: "DepMana",

  data() {
    return {
      dep: {  // 用于打开对话框的数据回填，也用于获取两个数据后，作为一个对象传后端进行添加
        name: '',  //双向绑定大画框中用户填写部门名称，即dept.name,也绑定从该数据库获取的部门
        parentId: null
      },
      allDeps: [],//用于添加对话框的下拉菜单展示用
      dialogVisible: false, //用于控制对话框的展示与消失
      filterText: '',
      deps: [],
      defaultProps: {
        label: "name",  //label是父节点的key,固定的.name是指定json中哪个字段作为label 有双引号
        children: "children"  //children同理 有双引号
      }
    }
  },
  /**
   * ui框架直接拷贝
   * watch 用来监控某一个变量的变化
   * 这里监控的就是 filterText 变量，即 filterText 变量发生变化的时候，就会触发当前方法
   * @param val 表示 filterText 当前最新的值
   */
  watch: {

    filterText(val) {
      // this.$refs 可以获取当前 Vue 对象中的所有 ref
      //this.$refs.tree 表示获取 ref 为 tree 的元素
      //调用当前树中的 filter 方法进行过滤
      //filter(val) 即使当找到tree元素后,有个filter-node-method 属性,执行这个属性的值所代表的方法.这里就是filterNode()
      this.$refs.tree.filter(val);
    }
  },
  mounted() {

    this.init_deps()

  },
  methods: {
    /**
     *
     * deps 这是所有的部门的 JSON  在进入递归后,deps就是上一次传进入的d.children ,
     * did 是刚刚删除的部门的 id
     *在进入递归时过程时,p 就是作为父部门担当也即是递归传入参数d，deps 就是 p 的 children ,也就是递归传入参数d.children
     *p 一开始是空的.在第一次时,从原始deps中拿出第一个节点放进d,也就是递归调用时的参数p,
     */
    removeDepFromDeps(p, deps, did) {
      //在进入递归后,deps就是上一次传进入的d.children ,如果是最后一个节点,children的长度是0 ,就会跳出循环.
      for (let i = 0; i < deps.length; i++) {
        //从上一次循环传入来的一串子节点 即 d.children 中,拿出第一个
        let d = deps[i];
        //说明当前的 d 对象就是要被删除的对象
        if (d.id == did) {
          //从 deps 这个数组的 第 i 个位置开始，移除1项数据
          deps.splice(i, 1);
          //如果这串子节点有三个,删了第二个,还有两个,这串字节的,的父节点p还是父节点
          //如果这串字节的只有一个,删了,这串字节的父节点p就变成了字节点.要把isparent设置为false. isparent的is省略.(不知为什么)
          if (deps.length == 0) {
            //说明这个 deps 中已经空了，即 p 的 children 已经空了
            p.parent = false; //这已经是个子节点.设置要把isparent设置为false. isparent的is省略.(不知为什么)
          }
          //找到要删除的节点,都删了,就不要递归下去了.
          return
        }else {
          //除了把d传进去,还要把d的子节点传进去,用于判断是否跳出循环的条件.
          this.removeDepFromDeps(d , d.children , did)
        }

      }
    },
    deleteDep(data) {
      this.$confirm('此操作将永久删除该部门, 是否继续?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        if (data.parent) {
          //证明这个选取的data所代表部门时有子部门的父部门。不能删除
          this.$message.error("该部门有子部门，不能删除") //ui组件语法。可查官网组件消息提示
        } else {
          //没有子部门，不是一个父部门
          this.deleteRequest("/system/basic/department/" + data.id).then((resp) => {
            if (resp) {
              //
              //进来这里，数据库删除成功，下面需要不刷新页面，不收起树删除页面中的节点。
              //data.id是要删除的部门的id  deps是原来初始化树时加载的所有树节点，数据库是删了，但这个节点还没跟着变。
              this.removeDepFromDeps({}, this.deps, data.id)
            }
          })
        }


      }).catch(() => {
        this.$message({
          type: 'info',
          message: '已取消删除'
        });
      });

    },
    addDep2Deps(deps, dep) {
      // for (const d in deps) {
      //   if (d.id == dep.parentId){
      //     d.children = d.children.concat(dep);
      //     return;
      //   }else {
      //     this.addDep2Deps(d.children,dep);
      //   }
      // }
      deps.forEach(d => {
        if (d.id == dep.parentId) {
          //将 dep 追加到 d 的 children 中
          d.children = d.children.concat(dep);
          return;
        } else {
          this.addDep2Deps(d.children, dep);
        }
      })
    },
    doAddDepartment() {
      this.postRequest("/system/basic/department/", this.dep).then((resp) => {
        if (resp) {   //记得要先判断resp有无东西，有才是正确的
          this.dialogVisible = false; //关对话框
          this.addDep2Deps(this.deps, resp.object); //填加到树里，树不收起来 , resp.这是后端传回来.包含了添加成功了的那个部门信息。
        }
      })
    },
    init_allDeps() {
      getRequest("/system/basic/department/all").then((resp) => {
        if (resp) {
          this.allDeps = resp;
        }
      })
    },
    showAddDepView(data) { //data所选择的节点的数据。
      this.dep.name = '';  //
      this.dep.parentId = data.id  //因为添加功能是添加子部门，所以将所选择的节点的id作为子部门的父id
      this.init_allDeps();  //打开对话框，就要从数据库中查出所有部门供对话框中的选择器展示
      this.dialogVisible = true;
    },
    /**
     * ui框架直接拷贝
     * @param value 当前的过滤关键字
     * @param data 一个部门的 JSON 对象
     * @returns {boolean} 返回 true 表示这个部门满足过滤条件，返回 false 表示这个部门不满足过滤条件
     */
    filterNode(value, data) {
      if (!value) return true;
      //判断 name 中是否包含 value，包含就返回 true，否则返回 false
      // data.name.这个name要根据json对象的key，
      return data.name.indexOf(value) !== -1;
    },
    init_deps() {
      this.getRequest("/system/basic/department/").then((resp) => {
        if (resp) {
          this.deps = resp;
        }
      })
    }
  }
}
</script>

<style scoped>

</style>